#include "jeditoperationwidget.h"
#include <QTECore/jlog.h>

#include <QApplication>
#include <QCloseEvent>
#include <QClipboard>
#include <QScreen>

#include <QPlainTextEdit>
#include <QTextEdit>
#include <QLineEdit>
#include <QLabel>

#include <QTimer>
#include <functional>

namespace QTE
{

class Q_DECL_HIDDEN JEditOperationWidgetPrivate
{
public:
	QWidget *m_widget = nullptr;
};

/*-------------------------------------------------------------------------------------------------------*/

#define ADD_ACTION(name, text, slot) \
({ \
	addAction(QCoreApplication::translate(name, text, nullptr), \
	this, slot); \
})

JEditOperationWidget::JEditOperationWidget(QWidget *widget) :
	d_ptr(new JEditOperationWidgetPrivate())
{
	Q_ASSERT(widget);
	setAttribute(Qt::WA_DeleteOnClose);

	if( J_OCT(QLineEdit*, widget) == nullptr and
		J_OCT(QTextEdit*, widget) == nullptr and
		J_OCT(QPlainTextEdit*, widget) == nullptr and
		J_OCT(QLabel*, widget) == nullptr )
	{
		jWarning() << "JEditOperationWidget: Widget is not 'QLineEdit/QTextEdit/QPlainTextEdit/QLabel'.";
		return ;
	}

	else if( J_OCT(QLabel*, widget) )
	{
		auto flags = J_RCT(QLabel*, widget)->textInteractionFlags();
		if( (flags & Qt::TextSelectableByMouse) == 0 and (flags & Qt::TextSelectableByKeyboard) == 0 )
			jWarning() << "JEditOperationWidget: QLabel::textInteractionFlags set on the label need to include either Qt::TextSelectableByMouse or Qt::TextSelectableByKeyboard.";
	}

	d_ptr->m_widget = widget;
	connect(widget, &QWidget::destroyed, this, &JEditOperationWidget::deleteLater);

	constexpr const char *name = "JEditOperationWidget";
	setObjectName(name);

	auto var = widget->property("QTE_TextInteraction");

	if( var.isValid() and var.toBool() )
	{
		ADD_ACTION(name, "end selection", [this]()
		{
			d_ptr->m_widget->setProperty("QTE_TextInteraction", false);
			emit endSelection();
		});

		addSeparator();
		std::function<void()> copyFunc = nullptr;

		if( J_OCT(QLineEdit*, widget) or J_OCT(QTextEdit*, widget) or J_OCT(QPlainTextEdit*, widget) )
		{
			copyFunc = [this]()
			{
				QMetaObject::invokeMethod(d_ptr->m_widget, "copy");
				d_ptr->m_widget->setProperty("QTE_TextInteraction", false);
				emit copy(QGuiApplication::clipboard()->text());
			};
		}
		else
		{
			copyFunc = [this]()
			{
				QGuiApplication::clipboard()->setText(J_RCT(QLabel*, d_ptr->m_widget)->selectedText());
				d_ptr->m_widget->setProperty("QTE_TextInteraction", false);
				emit copy(QGuiApplication::clipboard()->text());
			};
		}

		ADD_ACTION(name, "copy", copyFunc);

		if( J_OCT(QLineEdit*, widget) or
			(J_OCT(QPlainTextEdit*, widget) and not J_RCT(QPlainTextEdit*, widget)->isReadOnly()) or
			(J_OCT(QTextEdit*, widget) and not J_RCT(QTextEdit*, widget)->isReadOnly()) )
		{
			ADD_ACTION(name, "cut", [this]()
			{
				QMetaObject::invokeMethod(d_ptr->m_widget, "cut");
				d_ptr->m_widget->setProperty("QTE_TextInteraction", false);
				emit cut(QGuiApplication::clipboard()->text());
			});
		}
	}
	else
	{
		ADD_ACTION(name, "select", [this]()
		{
			d_ptr->m_widget->setProperty("QTE_TextInteraction", true);
			emit select();
		});
		addSeparator();
	}

	if( J_OCT(QLineEdit*, widget) or
		(J_OCT(QPlainTextEdit*, widget) and not J_RCT(QPlainTextEdit*, widget)->isReadOnly()) or
		(J_OCT(QTextEdit*, widget) and not J_RCT(QTextEdit*, widget)->isReadOnly()) )
	{
		ADD_ACTION(name, "paste", [this]()
		{
			QMetaObject::invokeMethod(d_ptr->m_widget, "paste");
			emit paste(QGuiApplication::clipboard()->text());
		});
	}

	ADD_ACTION(name, "select all", [this]()
	{
		d_ptr->m_widget->setProperty("QTE_TextInteraction", true);
		emit selectAll();

		(new JEditOperationWidget(d_ptr->m_widget))->show();
	});

	addSeparator();
	ADD_ACTION(name, "close menu", &JEditOperationWidget::close);

	widget->installEventFilter(this);

	if( J_OCT(QLineEdit*, widget) or J_OCT(QTextEdit*, widget) or J_OCT(QPlainTextEdit*, widget) )
		connect(this, SIGNAL(selectAll()), d_ptr->m_widget, SLOT(selectAll()));

	else if( J_OCT(QLabel*, widget) )
	{
		connect(this, &JEditOperationWidget::selectAll, this, [this]()
		{
			if( d_ptr->m_widget )
			{
				auto label = J_RCT(QLabel*, d_ptr->m_widget);
				label->setSelection(0, label->text().size());
			}
		});
	}
}

JEditOperationWidget::~JEditOperationWidget()
{
	delete d_ptr;
}

void JEditOperationWidget::setWidget(QWidget *widget)
{
	Q_ASSERT(widget);

	if( widget )
	{
		if( d_ptr->m_widget )
			d_ptr->m_widget->removeEventFilter(this);

		d_ptr->m_widget = widget;
		widget->installEventFilter(this);
	}
}

void JEditOperationWidget::showEvent(QShowEvent *event)
{
	QMenu::showEvent(event);

	auto pos = QCursor::pos();
	auto var = d_ptr->m_widget->property("QTE_TextInteraction");

	if( var.isValid() and var.toBool() )
	{
		if( J_OCT(QPlainTextEdit*, d_ptr->m_widget) )
		{
			auto edit = J_RCT(QPlainTextEdit*, d_ptr->m_widget);
			if( not edit->toPlainText().isEmpty() )
				pos = d_ptr->m_widget->mapToGlobal(edit->cursorRect().bottomRight());
		}

		else if( J_OCT(QTextEdit*, d_ptr->m_widget) )
		{
			auto edit = J_RCT(QTextEdit*, d_ptr->m_widget);
			if( not edit->toPlainText().isEmpty() )
				pos = d_ptr->m_widget->mapToGlobal(edit->cursorRect().bottomRight());
		}

//		else if( J_OCT(QLineEdit*, d_ptr->m_widget) )
//		{
//			pos = d_ptr->m_widget->mapToGlobal(d_ptr->m_widget->pos());
//			QFontMetrics metrics(d_ptr->m_widget->font());

//			auto edit = J_RCT(QLineEdit*, d_ptr->m_widget);
//			auto rect = metrics.boundingRect(edit->text().mid(0, edit->cursorPosition()));

//			pos.rx() += rect.width();
//			pos.ry() += rect.height();
//		}

		auto ssize = qApp->screens()[0]->size();

		if( pos.y() + height() + 3 > ssize.height() )
			pos.ry() = ssize.height() - height() + 3;
		else
			pos.ry() += 3;
	}

	else
	{
		pos.ry() -= height() + 3;

		if( pos.y() < 3 )
			pos.ry() = 3;
		else
			pos.ry() -= 3;
	}

	pos.rx() -= width() + 3;

	if( pos.x() < 3 )
		pos.rx() = 3;
	else
		pos.rx() -= 3;

	move(pos);
}

void JEditOperationWidget::closeEvent(QCloseEvent *event)
{
	emit closed();
	event->accept();
}

bool JEditOperationWidget::eventFilter(QObject *object, QEvent *event)
{
	if( object != d_ptr->m_widget )
		return false;

	Q_UNUSED(event);

	return false;
}

} //namespace QTE
